import { Dictionary } from "./Dictionary";
import { SceneManager } from "../manager/SceneManager";
import { ResManager } from "../manager/ResManager";
import PreLoadCountManager from "../manager/PreLoadCountManager";

/**
 * 中继数据
 */
class PoolTrunkData {
  prefab: cc.Prefab;
  list: Array<cc.Node>;
  reuse: boolean;
}

export class PoolPrefabObject {

  private static _instance: PoolPrefabObject = null;

  private _prefabObjectPoolDict: Dictionary<any, PoolTrunkData> = new Dictionary();

  private _timeDict = {} = {};

  private _prewarmNode: cc.Node = null;

  private _nResCurrLoadedCount: number = 0;

  private _isWorking: boolean = false;

  private _preloadList: Array<any> = [];

  public static getInstance(): PoolPrefabObject {

    if (!PoolPrefabObject._instance) {

      PoolPrefabObject._instance = new PoolPrefabObject();

      let rootSceneNode = SceneManager.getInstance().getCurrScene();
      PoolPrefabObject._instance._prewarmNode = cc.find("Prewarm", rootSceneNode);
      if (!PoolPrefabObject._instance._prewarmNode) {
        //console.error( "找不到节点Prewarm" );
      }
      else {
        cc.game.addPersistRootNode(PoolPrefabObject._instance._prewarmNode);
      }

    }

    return PoolPrefabObject._instance;
  }

  //设置工作状态，防止池子在意外的状态下被put进东西
  public setIsWorking(working: boolean) {
    cc.log("PoolPrefab Pool Working:" + working);
    this._isWorking = working;
  }

  /**
    * 通过唯一组件绑定预制体
    * @param key
    * @param prefab
    */
  public initializePrefabPoolWithComponent<T>(key: T, prefab: cc.Prefab, count: number = 1, reuse: boolean = false): void {
    if (prefab == null) {
      cc.warn("initializePrefabPoolWithComponent with null prefab:" + key);
      return;
    }

    let trunkData = this._prefabObjectPoolDict.get(key);
    if (!trunkData) {
      let trunkData = this.CreatePoolTrunkData();
      trunkData.prefab = prefab;
      trunkData.reuse = reuse;
      this._prefabObjectPoolDict.set(key, trunkData);

      this.doFactory(prefab, trunkData.list, count);
      cc.log(trunkData, 'xxxx');
    } else if (trunkData && !trunkData.prefab.data) {
      trunkData.prefab = prefab;
    }
  }

  /**
   * 通过路径绑定预制体
   * @param key
   * @param prefab
   */
  public initializePrefabPoolWithPath(key: string, prefab: cc.Prefab, reuse: boolean = false): void {
    let strList: Array<string> = key.split('/');
    let fileName: string = strList[strList.length - 1];

    if (!this._prefabObjectPoolDict.containsKey(fileName)) {
      let trunkData = this.CreatePoolTrunkData();
      trunkData.prefab = prefab;
      trunkData.reuse = reuse;
      this._prefabObjectPoolDict.set(fileName, trunkData);
    }
  }

  public destoryPrefabWithResPath(resPath): void {
    let fileName: string = this.GetKeyNameWithPath(resPath);
    let trunk: PoolTrunkData = this._prefabObjectPoolDict.get(fileName);
    if (trunk) {
      trunk.list.length = 0;
      this._prefabObjectPoolDict.delete(fileName);
    }
  }

  public dumpTime() {
    //cc.log( "加载时间:" + JSON.stringify( this._timeDict , null , '\t' ) );
  }

  //add by luoya，清除池子里面的内存和资源，这个方法会删除所有关联到的资源，慎用
  public clearPool(): void {
    this._timeDict = {};

    //先删除实例化出来的node
    let keyList: Array<any> = this._prefabObjectPoolDict.getKeyList();
    //尤其注意！警告！Dictionary是数组实现的，删除时候必须注意
    for (let k = keyList.length - 1; k >= 0; --k) {
      let key = keyList[k];
      let trunkData = this._prefabObjectPoolDict.get(key);
      if (!trunkData) {
        this._prefabObjectPoolDict.delete(key);
        continue;
      }

      // if( !trunkData.reuse )
      // {
      // 	this._prefabObjectPoolDict.delete( key );
      // }

      this.DestroyPoolTrunkData(trunkData);
    }
  }

  private _count = 0;
  public preload(resPath: string, num: number, reuse: boolean = false) {

    //cc.log(++this._count,resPath);

    if (resPath.length) {
      PreLoadCountManager.getInstance().addTotalResCount();
    }

    //this._timeDict[resPath] = Date.now();
    let prefab = this.lookUpPrefabWithPath(resPath);
    if (prefab) {
      PreLoadCountManager.getInstance().addResCurrLoadedCount();
      this.InitPrefabRes(resPath, prefab, num, reuse);
    } else {
      if (resPath.length) {
        let loadData: any = { f: resPath, num: num, reuse: reuse };
        this._preloadList.push(loadData);
      }

      if (this._nResCurrLoadedCount < 4 && this._preloadList.length) {
        let loadData: any = this._preloadList.shift();
        ++this._nResCurrLoadedCount;
        var self = this
        ResManager.getInstance().loadRes(loadData.f, function (errorMessage, loadedResource) {
          //cc.log("### PoolPrefabObject 预加载完成:",loadData.f);
          PreLoadCountManager.getInstance().addResCurrLoadedCount();

          --self._nResCurrLoadedCount;
          //检查资源加载
          if (errorMessage) {
            //console.warn( '载入预制资源失败, 原因:' + errorMessage );
            return;
          }
          self.InitPrefabRes(loadData.f, loadedResource, loadData.num, loadData.reuse);

          if (self._preloadList) {
            self.preload('', 0, false);
          }
        });
      }
    }
  }

  private InitPrefabRes(resPath, prefab, count, reuse) {
    // let nowDate = Date.now();
    // let originDate = this._timeDict[resPath];
    // this._timeDict[resPath] = ( nowDate - originDate ) / 1000;

    if (!(prefab instanceof cc.Prefab)) {
      //console.warn( '你载入的不是预制资源!' );
      return;
    }

    this.initializePrefabPoolWithPath(resPath, prefab, reuse);
    this.InitWithPath(resPath, count);
  }

  private GetKeyNameWithPath(filePath: string) {
    if (filePath == undefined) {
      cc.log('xxxx');
      return '';
    }
    let strList: Array<string> = filePath.split('/');

    let fileName: string = strList[strList.length - 1];

    return fileName;
  }

  public GetPoolLengthWithPath(filePath: string) {
    let fileName: string = this.GetKeyNameWithPath(filePath);
    let trunkData = this._prefabObjectPoolDict.get(fileName);
    if (!trunkData) {
      cc.error("Prefab存在而Object List不存在:" + fileName);
      return 0;
    }

    return trunkData.list.length;
  }

  private InitWithPath(filePath: string, num): cc.Node {
    let fileName: string = this.GetKeyNameWithPath(filePath);
    let trunkData = this._prefabObjectPoolDict.get(fileName);
    if (!trunkData) {
      cc.error("Prefab存在而Object List不存在:" + fileName);
      return null;
    }

    //luoya,修改成追加模式,因为参数num现在一般指单位角色需要的数量，并不是场上总数
    this.doFactoryWithPath(trunkData.prefab, trunkData.list, num, true);
  }

  /**
     * 工厂创建类实例
     * @param {{new(): T}} ctor
     * @param list
     */
  private doFactoryWithPath(prefab: cc.Prefab, list: Array<cc.Node>, num = 1, prewarm: boolean = false): void {

    for (let k = 0; k < num; ++k) {
      let node = cc.instantiate(prefab);
      list.push(node);


      node.active = false;
      //node.opacity = 0;
    }
  }

  public lookUpPrefabWithPath(resPath: string): cc.Prefab {
    let fileName: string = this.GetKeyNameWithPath(resPath);
    let trunkData = this._prefabObjectPoolDict.get(fileName);
    if (!trunkData) {
      return null;
    }

    return trunkData.prefab;
  }

  /**
      * 工厂创建类实例
      * @param {{new(): T}} ctor
      * @param list
      */
  private doFactory(prefab: cc.Prefab, list: Array<cc.Node>, count: number = 10): void {
    if (prefab == null) {
      cc.error("不能加载空的资源.");
      //console.trace();
      return;
    }

    //cc.warn( "池子里数量不够，开始加载:" + prefab.name );

    for (let k = 0; k < count; ++k) {
      let node = cc.instantiate(prefab);
      list.push(node);
      node.active = false;
      //node.opacity = 0;

      //let tranObj:any = node;
      //tranObj.putDate = new Date().getTime();
    }
  }

  /**
    * 通过路径存入对象
    * @param {cc.Node} obj
    */
  public putWithPath(obj: cc.Node, bRemoveFromParent: boolean = true): void {
    if (!this._isWorking) {
      return;
    }
    if (!obj) {
      return;
    }

    let key: string = obj.name;
    if (key == "") {
      return
    }

    if (obj.children == null) {
      return
    }

    //add by luoya for jump word optimize
    if (bRemoveFromParent) {
      obj.removeFromParent(false);
    }
    else {
      obj.setPosition(-9999999, -999999);
    }

    //let tranObj:any = obj;
    //tranObj.putDate = new Date().getTime();

    let trunkData = this._prefabObjectPoolDict.get(key)
    if (trunkData) {
      obj.active = false;
      //obj.opacity = 0;
      trunkData.list.push(obj);
    }
  }

  /**
     * 通过路径获取某类型实例对象
     * @param {{new(): T}} Class
     * @returns {T}
     */
  public getWithPath(filePath: string): cc.Node {
    if (!this._isWorking) {
      return;
    }

    let fileName: string = this.GetKeyNameWithPath(filePath);
    let trunkData = this._prefabObjectPoolDict.get(fileName);
    if (!trunkData) {
      return null;
    }

    if (trunkData.list.length < 1) {
      this.doFactoryWithPath(trunkData.prefab, trunkData.list);
    }

    let node = trunkData.list.shift();
    node.active = true;
    //node.opacity = 255;
    //cc.log('==============',node.name);
    return node;
  }

  protected CreatePoolTrunkData(): PoolTrunkData {
    let trunkData = new PoolTrunkData();
    trunkData.prefab = null;
    trunkData.list = new Array<cc.Node>();
    trunkData.reuse = false;

    return trunkData;
  }

  protected DestroyPoolTrunkData(trunkData) {
    if (!trunkData) {
      return;
    }

    for (let i = 0; i < trunkData.list.length; i++) {
      const node = trunkData.list[i];
      if (node && node.isValid) {
        node.removeFromParent()
        node.destroy();
      }
    }

    trunkData.list = new Array<cc.Node>();

  }

  /**
 * 获取某类型实例对象
 * @param {{new(): T}} Class
 * @returns {T}
 */
  public getWithComponent<T>(Class: T): cc.Node {
    if (!this._isWorking) {
      return;
    }
    let trunkData = this._prefabObjectPoolDict.get(Class);
    if (!trunkData) {
      return null;
    }

    if (trunkData.list.length < 1) {

      this.doFactory(trunkData.prefab, trunkData.list);
    }

    let node = trunkData.list.shift();
    node.active = true;
    //cc.log('==============',node.name);
    return node;
  }

  /**
   * 通过唯一组件存入对象
   * @param {cc.Node} obj
   */
  public putWithComponent(obj: cc.Node): void {
    if (!this._isWorking) {
      return;
    }
    let key = this.getObjectHomologousPrefab(obj);
    if (obj.parent) {
      obj.removeFromParent();
    }

    //let tranObj:any = obj;
    //tranObj.putDate = new Date().getTime();

    let pooNode: any = obj.getComponent(obj.name);
    pooNode.reset();

    let trunkData = this._prefabObjectPoolDict.get(key);
    if (trunkData) {
      trunkData.list.push(obj);
    }
  }

  private getObjectHomologousPrefab(obj: cc.Node): cc.Prefab {

    let prefabKey = null;
    let keyList = this._prefabObjectPoolDict.getKeyList();
    let findComponetInPrefab = 0;
    for (let k = 0; k < keyList.length; ++k) {
      let key = keyList[k];
      if (obj.getComponent(key)) {
        let trunkData = this._prefabObjectPoolDict.get(key);
        if (trunkData) {
          ++findComponetInPrefab;
          prefabKey = key;
        }
      }

    }

    if (findComponetInPrefab >= 2) {
      //cc.log('Warning : ','There is mult prefab banding this component!');
    }

    return prefabKey;
  }
}